"""
    五/六子棋模型
    ,基础的地图设计
    ,每次执子的记录与回滚
    ,配套的地图设计器
    ,智能设计的动态难度机器人
"""
import copy
import random

import pygame

from PyPixelCore.PyPixelController import PyPixelController
from PyPixelCore.Setting import Color_type
from PyPixelCore.animation_factory import *
from PyPixelCore.block import DynamicImageBlock, TextButton, Block
from PyPixelCore.panel.Panel import Panel
from PyPixelCore.timer.CycleTimer import CycleTimer
from connect5_6_chess_ai import ChessAI
from text_mapper import text_for_help

WHITE_CHESS = 1
BLACK_CHESS = -1

# 五子棋通用棋盘地图
class TempMap:
    def __init__(self):
        self.width = 15
        self.height = 15
        # 地图
        self.map = None
        # self.map = [[0 for col in range(self.height)] for row in range(self.width)]



# 五/六子棋棋盘模型
class Connect_5_6_Model:
    """
        维护五/六子棋棋盘
        维护进退步或者悔棋
        判断胜利
    """

    def __init__(self, difficulty_type=ChessAI.EASY_DIFFICULTY, easymap=TempMap()):
        # 加载地图
        self.width_num = easymap.width
        self.height_num = easymap.height
        self.legal_role = WHITE_CHESS
        if easymap.map is None:
            self.map = [[0 for col in range(self.height_num)] for row in range(self.width_num)]
        else:
            self.map = easymap.map
        self.is_mine_round = True
        self.is_started_game = False
        # 维护一个棋盘ai
        self.chess_ai = ChessAI(self, difficulty_type)
        self.success_index = 0.5

        # 维护每次落子的位置与当前的ai拷贝(栈)
        self.backtracking_stack = []  # 模型的拷贝 ai的拷贝
        self.copy_backtracking()
        self.regret_change = 3

    def init_chess(self, difficulty_type=ChessAI.EASY_DIFFICULTY, easymap=TempMap()):
        # 加载地图
        self.width_num = easymap.width
        self.height_num = easymap.height
        self.legal_role = WHITE_CHESS
        if easymap.map is None:
            self.map = [[0 for col in range(self.height_num)] for row in range(self.width_num)]
        else:
            self.map = easymap.map

        # 维护一个棋盘ai
        self.chess_ai = ChessAI(self, difficulty_type)
        self.success_index = 0.5

        # 维护每次落子的位置与当前的ai拷贝(栈)
        self.backtracking_stack = []  # 模型的拷贝 ai的拷贝
        self.copy_backtracking()
        self.regret_change = 3

    def do_chess(self, point_x, point_y):
        # 落子模拟
        if not self.is_started_game: return False
        if point_x < 0 or point_y < 0:  return False
        if point_x > 15 and point_y > 15:  return False
        if self.map[point_y][point_x] != 0:  return False
        self.copy_backtracking()
        self.map[point_y][point_x] = self.legal_role
        # 更新chess_map
        self.legal_role *= -1
        self.chess_ai.update_value_map(point_x, point_y)
        return True

    def reset(self, easymap = TempMap()):
        self.legal_role = WHITE_CHESS
        self.is_started_game = False
        if easymap.map is None:
            self.map = [[0 for col in range(self.height_num)] for row in range(self.width_num)]
        else:
            self.map = easymap.map
        self.chess_ai.reset()
        self.backtracking_stack = []  # 清空回溯拷贝

    def show_map(self):
        print(
            "图:")
        for h in range(self.height_num):
            for w in range(self.width_num):
                value_white = self.map[h][w]
                print(""if value_white<0 else " ",value_white, end="")
            print("         ",end="")
            print()

    def copy_backtracking(self):
        # 拷贝当前的棋子,ai价值图和总图
        self.backtracking_stack.append([copy.deepcopy(self.map), copy.deepcopy(self.chess_ai.white_value_map), copy.deepcopy(self.chess_ai.black_value_map)])

    def back_tracking(self):
        # 回溯到上一步
        if len(self.backtracking_stack) == 1:
            self.map = self.backtracking_stack[0][0]
            self.chess_ai.white_value_map = self.backtracking_stack[0][1]
            self.chess_ai.black_value_map = self.backtracking_stack[0][2]
            return False
        value = self.backtracking_stack.pop()
        self.map = value[0]
        self.chess_ai.white_value_map = value[1]
        self.chess_ai.black_value_map = value[2]
        # value[3].kill()
        return True

    def do_ai_chess(self):
        self.chess_ai.do_ai_chess()

    @property
    def success_role(self):
        for i in range(self.width_num):
            for y in range(self.height_num):
                value_b = self.chess_ai.black_value_map[i][y]
                if value_b < 0: continue
                value_w = self.chess_ai.white_value_map[i][y]
                if ChessAI.max_dir_value(value_b) >= 5: return 1
                elif ChessAI.max_dir_value(value_w) >= 5: return -1
        return None







def test():
    ppc = PyPixelController()

    tile_size = 20
    tile_width = 2
    end_size = 14*(tile_size+tile_width)


    chess_model = Connect_5_6_Model(difficulty_type=ChessAI.NORMAL_DIFFICULTY)
    chess_model.init_chess()
    test_game_panel = Panel(width=458,height=332,point_x=140,point_y=50,border_width=2, layer=1,bk_color=Color_type.VERY_LIGHT_GREEN)


    chess_board_surf = pygame.Surface((end_size+tile_width,end_size+tile_width))
    chess_board_surf.fill(Color_type.RICE_YELLOW)
    for col in range(15):
        pygame.draw.line(chess_board_surf, Color_type.BLACK,[(tile_size+tile_width)*col,0], [(tile_size+tile_width)*col,end_size], width=tile_width)
    for row in range(15):
        pygame.draw.line(chess_board_surf,Color_type.BLACK,[0, (tile_size+tile_width)*row],[end_size ,(tile_size+tile_width)*row], width=tile_width)
    test_game_panel.bk_image.blit(chess_board_surf,
                                  (test_game_panel.bk_image.get_rect().center[0]-chess_board_surf.get_width()/2
                                   ,test_game_panel.bk_image.get_rect().center[1]-chess_board_surf.get_height()/2))
    test_game_panel.logical_chess_pos = (test_game_panel.bk_image.get_rect().center[0]-chess_board_surf.get_width()/2 - tile_size/2 + test_game_panel.border_width
                                   ,test_game_panel.bk_image.get_rect().center[1]-chess_board_surf.get_height()/2 - tile_size/2  + test_game_panel.border_width )
    test_game_panel.is_dirty = True

    clicked_inform = DynamicImageBlock(width=400, height=400, point_y=111, point_x=111, layer=2, is_alpha=False)
    clicked_inform.add_image_list_from_file(f"./recourse/clicked", "clicked", limit=True)
    clicked_inform.set_video_state("clicked")
    clicked_inform.set_animation_interval(6)
    clicked_inform.is_show = False
    def monitor_clicked_inform(self):
        if clicked_inform.now_frame_index == clicked_inform.max_frame_index:
            clicked_inform.is_show = False
            clicked_inform.delete_frame_animation()
    def clicked_inform_click_animation(pos):
        clicked_inform.is_show = True
        clicked_inform.rect.center = pos
        clicked_inform.now_frame_index = 0
        clicked_inform.set_frame_animation(monitor_clicked_inform)

    # 工具按钮
    button_connect5_help = TextButton("帮助", point_x=7, point_y=10)
    button_connect5_fail = TextButton("认输", point_x=57, point_y=10)
    button_connect5_start = TextButton("开始", point_x=7, point_y=40)
    button_connect5_reset = TextButton("重置", point_x=57, point_y=40)
    button_connect5_traceback = TextButton("悔棋", point_x=7, point_y=70)
    connect5_tool_interface = Panel(width=105, height=178, point_x=602, point_y=204, border_width=2)
    connect5_tool_interface.add_sprites(button_connect5_help, button_connect5_fail,button_connect5_start, button_connect5_reset, button_connect5_traceback)

    def ss(self):
        chess_model.show_map()
        print(len(chess_model.backtracking_stack))
    button_connect5_fail.set_click_down_func(ss)

    # def yes_choice_func():
    #     print("用户点击了确定")
    # def no_choice_func():
    #     print("用户取消了选项")
    # help_piece = DialogFactory.dialog(text_for_help, ppc, size_inner=15, dialog_type=Dialog_Type.CONFIRM_DIALOG, yes_choice_func=yes_choice_func, no_choice_func=no_choice_func)
    def clicked_help_button(self):
        def yes_choice_func():
            print("用户点击了确定")
        def no_choice_func():
            print("用户取消了选项")
        help_piece = DialogFactory.dialog(text_for_help, ppc, size_inner=15, dialog_type=Dialog_Type.CONFIRM_DIALOG,yes_choice_func=yes_choice_func, no_choice_func=no_choice_func)
        help_piece.run(ppc)
    button_connect5_help.set_click_up_func(clicked_help_button)

    def clicked_traceback_button(self):
        def yes_choice_func():
            disappearing_animation("!!!! 悔棋 !!!!", test_game_panel,font_size=30, bk_color=Color_type.LIGHT_GREY, font_color=Color_type.RED)
            if chess_model.is_mine_round:
                if chess_model.back_tracking(): test_game_panel.temp_chess_arr.pop().kill()
            if chess_model.back_tracking():
                chess_model.chess_ai.close()
            test_game_panel.temp_chess_arr.pop().kill()
            chess_model.regret_change -= 1
        inform_dialog_animation(f"\n\n\n\n\n你还有 {chess_model.regret_change} 次反悔的机会,是否使用?", ppc, size_inner=15,dialog_type=Dialog_Type.CHOOSE_DIALOG,yes_choice_func=yes_choice_func)
    button_connect5_traceback.set_click_down_func(clicked_traceback_button)

    # 用于提醒位置的黑白棋
    inform_chess_black = Block(20, 20, is_alpha=True, bk_color=(255, 255, 255, 0), layer=2)
    inform_chess_black.load_image("./recourse/black_chess.png")
    inform_chess_black.image.set_alpha(150)
    inform_chess_white = Block(20, 20, is_alpha=True, bk_color=(255, 255, 255, 0), layer=2)
    inform_chess_white.load_image("./recourse/white_chess.png")
    inform_chess_white.image.set_alpha(150)
    inform_chess_black.is_show = False
    inform_chess_white.is_show = False
    test_game_panel.before_chess = inform_chess_white
    test_game_panel.logical_chess_pos_before = [-1,-1]

    # 聚焦时移动的放置位置动画(性能开销可能有点大)
    def test_inform_chess_pos(self: Panel, pos):
        if not chess_model.is_started_game: return
        if not self.rect.collidepoint(pos):
            self.before_chess.is_show = False
            return
        if not self.before_chess.is_show:
            self.before_chess.is_show = True
        logical_pos = [pos[0] - self.point_x, pos[1] - self.point_y]
        logical_chess_pos = [int((logical_pos[0] - test_game_panel.logical_chess_pos[0]) // (tile_size + tile_width)),
                             int((logical_pos[1] - test_game_panel.logical_chess_pos[1]) // (tile_size + tile_width))]

        # 绘制提示
        if -1 < logical_chess_pos[0] < 15 and -1 < logical_chess_pos[1] < 15:
            if self.logical_chess_pos_before == logical_chess_pos:
                return
            if chess_model.map[logical_chess_pos[1]][logical_chess_pos[0]] != 0:
                self.logical_chess_pos_before = [-1, -1]
                test_game_panel.before_chess.rect.x = pos[0] - tile_size / 2
                test_game_panel.before_chess.rect.y = pos[1] - tile_size / 2
                return
            self.logical_chess_pos_before = logical_chess_pos
            test_game_panel.before_chess.rect.x = logical_chess_pos[0]*(tile_size+tile_width)+test_game_panel.logical_chess_pos[0] + self.point_x - tile_width/2
            test_game_panel.before_chess.rect.y = logical_chess_pos[1]*(tile_size+tile_width) + test_game_panel.logical_chess_pos[1] + self.point_y - tile_width/2
        else:
            self.logical_chess_pos_before = [-1,-1]
            test_game_panel.before_chess.rect.x = pos[0] - tile_size/2
            test_game_panel.before_chess.rect.y = pos[1] - tile_size/2

    # 开始游戏测试按钮(当前默认为人机模式)
    def start_connect5_game(self: TextButton = None):
        if chess_model.is_started_game:
            flex_animation("游戏已经开始了！！！", test_game_panel)
            return
        chess_model.is_started_game = True
        if not random.choice([True,False]):  # 后手由ai先落子, ai默认第一颗子落在中间
            do_chess_by_logical(7,7)
            disappearing_animation(f"开始游戏, 当前执黑(后手)", test_game_panel, bk_color=Color_type.WHITE, font_color=Color_type.BLACK)
        else:  disappearing_animation(f"开始游戏, 当前执白(先手)", test_game_panel, bk_color=Color_type.BLACK, font_color=Color_type.WHITE)
        chess_model.is_mine_round = True
        ppc.reset_focus_panel(test_game_panel)
    button_connect5_start.set_click_up_func(start_connect5_game)

    # 根据逻辑位置放置棋子
    work_piece = TextAnimationFactory.flex_text("不合格的位置", test_game_panel)
    test_game_panel.temp_chess_arr = []
    def do_chess_by_logical(point_x,point_y):
        if not chess_model.do_chess(point_y, point_x):
            work_piece.run(test_game_panel)
            return False
        chess_model.is_mine_round = not chess_model.is_mine_round
        # 创建一个棋子的图片精灵并绘制到上面
        aim_chess = Block(18, 18, is_alpha=True, bk_color=(255, 255, 255, 255),
                          point_x=point_y * (tile_size + tile_width) +
                                  test_game_panel.logical_chess_pos[0]
                          , point_y=point_x * (tile_size + tile_width) +
                                    test_game_panel.logical_chess_pos[1])
        aim_chess.load_image(
            "./recourse/black_chess.png" if chess_model.legal_role == WHITE_CHESS else "./recourse/white_chess.png",
            True)
        test_game_panel.add_sprites(aim_chess)
        test_game_panel.temp_chess_arr.append(aim_chess)
        chess_model.chess_ai.show_value_map()
        # 更换应当显示的棋子
        test_game_panel.before_chess.is_show = False
        temp_center = test_game_panel.before_chess.rect.center
        test_game_panel.before_chess = inform_chess_white if chess_model.legal_role == WHITE_CHESS else inform_chess_black
        test_game_panel.before_chess.is_show = True
        # 修改当前鼠标上应当显示的棋子
        test_game_panel.before_chess.rect.center = temp_center
        # 放置棋子后检测是否胜利
        success_role = chess_model.success_role
        if success_role is not None:
            if success_role == -1:  disappearing_animation(f"黑棋胜利", test_game_panel, bk_color=Color_type.WHITE, font_color=Color_type.BLACK, offset_y=0.1, show_time=1.5)
            else:  disappearing_animation(f"白棋胜利", test_game_panel, bk_color=Color_type.BLACK, font_color=Color_type.WHITE,offset_y=0.1,show_time=1.5)
            # 胜利结束后的善后工作(暂无)
        return True

    # 重新开始游戏
    def new_game(self: TextButton = None):
        def do_yes():
            chess_model.reset()
            start_connect5_game()
            test_game_panel.is_dirty = True
        work_space = DialogFactory.dialog("\n\n\n\n你确定要重新开始吗?", aim_controller=ppc, yes_choice_func=do_yes,dialog_type=Dialog_Type.CHOOSE_DIALOG)
        work_space.run(ppc)
    button_connect5_reset.set_click_up_func(new_game)

    # 点击放置棋子的函数
    def test_game_panel_main_job(self: Panel, pos):
        if pos is None: return
        if not chess_model.is_started_game: return
        clicked_inform_click_animation(pos)
        if not chess_model.is_mine_round:  # 当不是自己的回合但是在进行落子的时候,进行提醒然后返回
            flex_animation("当前不是你的回合!!!!", test_game_panel)
            return
        point_x = self.logical_chess_pos_before[0]
        point_y = self.logical_chess_pos_before[1]
        if not do_chess_by_logical(point_y,point_x):  return
        # 放置棋子结束后调用ai进行ai策略落子
        chess_model.do_ai_chess()
        test_game_panel.set_timer_animation(CycleTimer(60), ai_chess_monitor)

    def leave_focus_animation(self: Panel, pos):  # 离开焦点隐藏提醒用的棋子
        inform_chess_white.is_show = False
        inform_chess_black.is_show = False

    def ai_chess_monitor(self: Panel):  # 一秒监控一次ai的落子点
        print("侦察ai的落子点...")
        if chess_model.chess_ai.ai_do_chess_pos is None:  return
        print(chess_model.chess_ai.ai_do_chess_pos)
         # 执行ai落子
        do_chess_by_logical(chess_model.chess_ai.ai_do_chess_pos[0], chess_model.chess_ai.ai_do_chess_pos[1])
        self.delete_timer_animation(ai_chess_monitor)
        chess_model.chess_ai.ai_do_chess_pos = None
        print("侦察落点结束")

    def close(self: PyPixelController):  #  关闭程序的时候执行的事情
        chess_model.chess_ai.close()  # 关闭ai中计算的线程

    test_game_panel.focus_animation_do = test_game_panel_main_job
    test_game_panel.focus_motion_animation_do = test_inform_chess_pos
    test_game_panel.leave_focus_animation_do = leave_focus_animation
    # test_game_panel.set_timer_animation(CycleTimer(60), ai_chess_monitor)


    ppc.add_block(test_game_panel, clicked_inform, connect5_tool_interface,inform_chess_white, inform_chess_black)
    ppc.close_func = close

    # 强制聚焦在test_game_panel上
    ppc.focus_block = test_game_panel
    inform_chess_white.is_show = True

    ppc.run()

if __name__ == '__main__':
    test()
